package api

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/julienschmidt/httprouter"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
	"notabug.org/ropemann/imabon/src/common"
	"notabug.org/ropemann/imabon/src/entities/post"
	"notabug.org/ropemann/imabon/src/entities/thread"
)

type ApiTestSuite struct {
	suite.Suite
	prefix   string
	board    string
	threadId string
	router   httprouter.Router
	server   httptest.Server
}

func (s *ApiTestSuite) SetupSuite() {
	common.LoadConfig("../../../.env")
	s.board = "ani"
	s.threadId = "625b8273c84324aea98ce004"
	s.router = *httprouter.New()
	RegisterApiRoutes(&s.router)
	s.prefix = os.Getenv("API_PREFIX")
	s.server = *httptest.NewServer(&s.router)

	PersistThread = func(board string, newThread thread.Thread) (string, error) {
		if board == s.board {
			return "success", nil
		}
		return "fail", errors.New("fail")
	}

	Create = func(board, thread string, newPost post.Post) (int64, error) {
		if board == s.board {
			return 1, nil
		}
		return 0, errors.New("fail")
	}
}

func (s *ApiTestSuite) TestRegisterApiRoutes() {
	h, p, b := s.router.Lookup("GET", "/api/boards")

	assert.NotNil(s.T(), h)
	assert.Empty(s.T(), p)
	assert.False(s.T(), b)
}

func (s *ApiTestSuite) TestGetAllBoards() {
	r, err := http.Get(fmt.Sprintf("%s/%s/boards", s.server.URL, s.prefix))
	assert.Nil(s.T(), err)
	assert.Equal(s.T(), 200, r.StatusCode)
}

func (s *ApiTestSuite) TestGetMostRecentThreads() {
	r, err := http.Get(fmt.Sprintf("%s/%s/recent", s.server.URL, s.prefix))
	assert.Nil(s.T(), err)
	assert.Equal(s.T(), 200, r.StatusCode)
}

func (s *ApiTestSuite) TestGetAllThreadsByBoard() {
	r, err := http.Get(fmt.Sprintf("%s/%s/content/%s", s.server.URL, s.prefix, s.board))
	assert.Nil(s.T(), err)
	assert.Equal(s.T(), 200, r.StatusCode)
}

func (s *ApiTestSuite) TestGetThreadById() {
	r, err := http.Get(fmt.Sprintf("%s/%s/content/%s/%s", s.server.URL, s.prefix, s.board, s.threadId))
	assert.Nil(s.T(), err)
	assert.Equal(s.T(), 200, r.StatusCode)
}

func (s *ApiTestSuite) TestSendJsonResponseFail() {
	h := httprouter.New()
	h.GET("/fail", func(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
		sendJsonResponse(w, http.StatusInternalServerError, make(chan int))
	})
	server := *httptest.NewServer(h)

	r, _ := http.Get(fmt.Sprintf("%s/fail", server.URL))
	assert.Equal(s.T(), http.StatusInternalServerError, r.StatusCode)
}

func (s *ApiTestSuite) TestPersistThread() {
	body := []byte(`{"threadTitle": "test", "authorName": "test", "comment": "test"}`)
	bodyReader := bytes.NewReader(body)
	r, err := http.Post(
		fmt.Sprintf("%s/%s/thread/%s", s.server.URL, s.prefix, s.board),
		"application/json", bodyReader)

	assert.Nil(s.T(), err)
	assert.Equal(s.T(), r.StatusCode, http.StatusOK)

}

func (s *ApiTestSuite) TestPersistThreadFail() {
	body := []byte(`{"threadTitle": "test", "authorName": "test", "comment": "test"}`)
	bodyReader := bytes.NewReader(body)
	r, err := http.Post(
		fmt.Sprintf("%s/%s/thread/fail", s.server.URL, s.prefix),
		"application/json", bodyReader)

	assert.Nil(s.T(), err)
	assert.Equal(s.T(), r.StatusCode, http.StatusBadRequest)
}

func (s *ApiTestSuite) TestCreatePost() {
	body := []byte(`{"authorName": "test", "comment": "test"}`)
	bodyReader := bytes.NewReader(body)
	r, err := http.Post(
		fmt.Sprintf("%s/%s/post/%s/12345", s.server.URL, s.prefix, s.board),
		"application/json", bodyReader)

	assert.Nil(s.T(), err)
	assert.Equal(s.T(), r.StatusCode, http.StatusOK)
}

func (s *ApiTestSuite) TestCreatePostFail() {
	body := []byte(`{"authorName": "test", "comment": "test"}`)
	bodyReader := bytes.NewReader(body)
	r, err := http.Post(
		fmt.Sprintf("%s/%s/post/fail/12345", s.server.URL, s.prefix),
		"application/json", bodyReader)

	assert.Nil(s.T(), err)
	assert.Equal(s.T(), r.StatusCode, http.StatusBadRequest)
}

func TestApiTestSuite(t *testing.T) {
	suite.Run(t, new(ApiTestSuite))
}
